
/* Includes ------------------------------------------------------------------*/
#include "eeprom.h"

#include "debug.h"
#include "mf_config.h"

//============flash 操作接口实现========
static uint8_t Flash_erase(uint32_t Page_Address, uint16_t page_cnt)
{
    for (uint16_t i = 0; i < page_cnt; i++) {
        FL_FLASH_SectorErase(FLASH, Page_Address + i * FLASH_SECTOR_SIZE);
    }
}

static uint16_t Flash_Read(uint32_t address, uint32_t *dat, uint16_t len)
{
    uint16_t ret_val = 0;
    if (dat == NULL || len == 0) return 0;
    if (address >= FALSH_USR_PAGE2_START + USR_FLASH_HALF_SIZE) {
        *dat = 0xffffffff;
        return 1;
    }

    for (uint8_t i = 0; i < len; ++i) {
        //  LOG("read [0x%x] = 0x%x\n",address,dat[i]);

        dat[i] = *((uint32_t volatile *)(address + i * FLASH_PROGRAM_SIZE));
        ret_val += 4;
    }

    return 1;
}

static uint8_t Flash_Write_1Word(uint32_t address, uint32_t dat)
{
    LOG("[%s:%x]%X\r\n", __FUNCTION__, address, dat);

    uint8_t status = FL_FLASH_Program_Word(FLASH, address, dat);
    if (status != FL_PASS) return 0;

    return 1;
}

static uint8_t Flash_Write(uint32_t address, uint32_t *dat, uint16_t len)
{
    for (uint16_t i = 0; i < len; i++) {
        Flash_Write_1Word(address + i * FLASH_PROGRAM_SIZE, *dat++);
    }
}

//================
VM_HDL_t vm_hdl;
uint8_t  temp_buf[200];
//=================

void VME2P_Initialize(void)
{
    uint32_t id = 0, data_len = 0, crc = 0;
    uint32_t r_addr, w_addr;
    uint32_t i;

    memset(&vm_hdl, 0x00, sizeof(vm_hdl));

    Flash_Read(USR_FLASH_BASE_ADDRESS, &vm_hdl.valid_flag, 1);

    if (vm_hdl.valid_flag == VM_VALID_FLAG) {
        r_addr = USR_FLASH_BASE_ADDRESS + sizeof(vm_hdl.valid_flag);
        LOG("vm first half init start\n");
        while (1) {
L_VMINIT:
            Flash_Read(r_addr, &id, 1);
            //	LOG("r_addr =0X%X,id=%d\n",r_addr,id);
            if (id != 0 && id != 0xFFFFFFFF) {
                if (id < VM_INDEX_MAX) {
                    vm_hdl.vm_mage[id].id_addr = r_addr;
                    r_addr += sizeof(id);
                    Flash_Read(r_addr, &data_len, 1);
                    r_addr += sizeof(data_len);
                    vm_hdl.vm_mage[id].data_len = data_len;
                    data_len =
                        ((data_len + FLASH_PROGRAM_SIZE - 1) / FLASH_PROGRAM_SIZE) * FLASH_PROGRAM_SIZE;
                    r_addr = r_addr + data_len + sizeof(crc);
                }
                else {
                    vm_hdl.err_flag  = 1; // 需要异常处理？
                    vm_hdl.last_addr = r_addr;
                    LOG("id over max:%x, %x\n", r_addr, id);
                    break;
                }
            }
            else {
                vm_hdl.last_addr = r_addr;
                LOG("vm init end:%x\n", vm_hdl.last_addr);
                break;
            }
        }
    }
    else {
        memset(&vm_hdl.valid_flag, 0x00, sizeof(vm_hdl.valid_flag));
        Flash_Read(USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE, &vm_hdl.valid_flag, 1);
        if (vm_hdl.valid_flag == VM_VALID_FLAG) {
            r_addr = USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE + sizeof(vm_hdl.valid_flag);
            LOG("vm lower half init start\n");
            goto L_VMINIT;
        }
        else {
            w_addr = USR_FLASH_BASE_ADDRESS;
            Flash_erase(FLASH_USR_PAGE1_START, FLASH_HALF_SECTOR_NUM);
            Flash_erase(FALSH_USR_PAGE2_START, FLASH_HALF_SECTOR_NUM);
            Flash_Write_1Word(w_addr, VM_VALID_FLAG);
            vm_hdl.last_addr = w_addr + sizeof(vm_hdl.valid_flag);
            LOG("vm init first:%x\n", vm_hdl.last_addr);
        }
    }
}

uint32_t VME2P_Read(uint32_t index, uint8_t *buf, uint32_t len)
{
    uint32_t i, crc = 0, r_crc = 0;
    uint32_t r_addr, data_len;
    uint32_t id = 0;

    if (index > VM_INDEX_MAX) {
        LOG("index over max:%d\n", index);
        return 0;
    }
    r_addr = vm_hdl.vm_mage[index].id_addr;
    if (r_addr < USR_FLASH_BASE_ADDRESS || r_addr > USR_FLASH_BASE_ADDRESS + USR_FLAH_NUM_SIZE) {
        // LOG("index [%d] r_addr err:%x\n", index, r_addr);
        return 0;
    }
    Flash_Read(r_addr, &id, 1);
    if (id != index) {
        LOG("id inconformity:%d,%d\n", index, id);
        return 0;
    }
    data_len = vm_hdl.vm_mage[id].data_len;
    if (data_len != len) {
        LOG("data len inconformity:%d,%d\n", data_len, len);
        return 0;
    }
    r_addr   = r_addr + sizeof(id) + sizeof(len);
    data_len = ((data_len + FLASH_PROGRAM_SIZE - 1) / FLASH_PROGRAM_SIZE) * FLASH_PROGRAM_SIZE;
    Flash_Read(r_addr, (uint32_t *)temp_buf, data_len / 4);
    for (i = 0; i < len; i++) {
        crc += temp_buf[i];
    }
    // data_len = ((data_len + 3)/4)*4;
    r_addr += data_len;
    Flash_Read(r_addr, &r_crc, 1);
    if (r_crc != crc) {
        LOG("crc inconformity:%d,%d\n", r_crc, crc);
        return 0;
    }
    memcpy(buf, temp_buf, len);
    return len;
}

void vm_clean(uint32_t start_addr)
{
    uint8_t  p_flag = 0;
    uint32_t clean_addr;
    uint32_t valid_flag;
    uint32_t index, len, len_num, id, crc;

    if (start_addr > USR_FLASH_BASE_ADDRESS &&
        start_addr < (USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE)) {
        clean_addr = USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE + sizeof(valid_flag);
        p_flag     = 1;
        LOG("clean first half start:%x\n", clean_addr);
    }
    else if (start_addr > (USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE) &&
             start_addr < (USR_FLASH_BASE_ADDRESS + USR_FLAH_NUM_SIZE)) {
        clean_addr = USR_FLASH_BASE_ADDRESS + sizeof(valid_flag);
        LOG("clean lower half start:%x\n", clean_addr);
    }
    else {
        LOG("clean start_addr err!!!\n");
        return;
    }
    for (index = 0; index < VM_INDEX_MAX; index++) {
        if (vm_hdl.vm_mage[index].id_addr) {
            len     = ((vm_hdl.vm_mage[index].data_len + 3) / 4) * 4;
            len_num = sizeof(id) + sizeof(len) + len + sizeof(crc);
            memset(temp_buf, 0x0, len_num);
            Flash_Read(vm_hdl.vm_mage[index].id_addr, (uint32_t *)temp_buf, len_num / 4);
            Flash_Write(clean_addr, (uint32_t *)temp_buf, len_num / 4);
            vm_hdl.vm_mage[index].id_addr = clean_addr;
            clean_addr += len_num;
        }
    }

    vm_hdl.last_addr = clean_addr;

    if (p_flag) {
        clean_addr = USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE;
        Flash_Write_1Word(clean_addr, VM_VALID_FLAG); // 如果在这里断电？
        Flash_erase(FLASH_USR_PAGE1_START, FLASH_HALF_SECTOR_NUM);
        LOG("clean first half end:%x\n", clean_addr);
    }
    else {
        clean_addr = USR_FLASH_BASE_ADDRESS;
        Flash_Write_1Word(clean_addr, VM_VALID_FLAG);
        Flash_erase(FALSH_USR_PAGE2_START, FLASH_HALF_SECTOR_NUM);
        LOG("clean lower half end:%x\n", clean_addr);
    }
}

uint32_t VME2P_Write(uint32_t index, const uint8_t *buf, uint32_t len)
{
    uint32_t w_addr, w_len, crc = 0;
    uint32_t remain, i;

    if (index > VM_INDEX_MAX) {
        LOG("w index over max:%d\n", index);
        return 0;
    }
    w_addr = vm_hdl.last_addr;
    LOG("vm write start:%x\n", w_addr);
    if (w_addr >= USR_FLASH_BASE_ADDRESS && w_addr < (USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE)) {
        remain = (USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE) - w_addr;
    }
    else if (w_addr >= (USR_FLASH_BASE_ADDRESS + USR_FLASH_HALF_SIZE) &&
             w_addr < (USR_FLASH_BASE_ADDRESS + USR_FLAH_NUM_SIZE)) {
        remain = (USR_FLASH_BASE_ADDRESS + USR_FLAH_NUM_SIZE) - w_addr;
    }
    else {
        return 0;
    }
    w_len = sizeof(w_addr) + sizeof(len) + (((len + 3) / 4) * 4) + sizeof(crc);
    if (w_len > remain) {
        LOG("need clean vm:%d, %d", w_len, remain);
        vm_clean(w_addr);
        w_addr = vm_hdl.last_addr;
    }

    Flash_Write_1Word(w_addr, index);
    vm_hdl.vm_mage[index].id_addr = w_addr;
    w_addr += sizeof(index);
    Flash_Write_1Word(w_addr, len);
    vm_hdl.vm_mage[index].data_len = len;
    // 这片代码可以优化，先把参数赋给对应的ram空间后，一次性把数据全部写入
    w_addr += sizeof(len);
    w_len = ((len + 3) / 4) * 4;
    memset(temp_buf, 0x0, w_len);
    memcpy(temp_buf, buf, len);
    Flash_Write(w_addr, (uint32_t *)temp_buf, w_len / 4);
    w_addr += w_len;
    for (i = 0; i < len; i++) {
        crc += buf[i];
    }
    Flash_Write_1Word(w_addr, crc);
    w_addr += sizeof(crc);
    vm_hdl.last_addr = w_addr;
    return len;
}
